#ifndef BACKPACK_USERACTIVITY_FACTORY_H_
#define BACKPACK_USERACTIVITY_FACTORY_H_

#include "UserActivity.hpp"
#include "Action.hpp"

#include <queue>
#include <boost/variant/static_visitor.hpp>

namespace backpack {




class SimpleUserActivityFactory :
  public boost::static_visitor<UserActivityVariant> {
public:
  UserActivityVariant operator()(WayPoint const& wayPoint) const {
    return WayPointActivity(wayPoint);
  }
  UserActivityVariant operator()(TouchPoint const& touchPoint) const {
    return TouchActivity(touchPoint);
  }
  UserActivityVariant operator()(UnlockPoint const& unlockPoint) const {
    return UnlockActivity(unlockPoint);
  }
}; // struct UserActivityFactory





class ChainOfResponsibilityUserActivityFactory :
  public boost::static_visitor<UserActivityVariant> {
public:
  template <class Variant>
  UserActivityVariant operator() (HotSpot<Variant> & hotspot) const;

private:
  SimpleUserActivityFactory m_createUserActivity;

  class ChainBuilder : public boost::static_visitor<> {
  public:
    ChainBuilder(std::queue<UserActivityVariant> &queue,
      SimpleUserActivityFactory const& activityFactory)
      : m_rQueue(queue), m_createUserActivity(activityFactory) {
    }

    template <class Variant>
    void operator() (HotSpot<Variant> const& hotspot) const;
  private:
    std::queue<UserActivityVariant> &m_rQueue;
    SimpleUserActivityFactory const& m_createUserActivity;
  }; // class ChainBuilder

}; // class ChainOfResponsibilityUserActivityFactory




template <class Variant>
UserActivityVariant ChainOfResponsibilityUserActivityFactory::operator() (
    HotSpot<Variant> & hotspot) const {

  Variant &variant = static_cast<Variant&>(hotspot);
  if (hotspot.isEnabled() || !hotspot.hasChildHotSpot()) {
    return m_createUserActivity(variant);
  }

  typedef std::queue<UserActivityVariant> ActivityQueue;
  std::shared_ptr<ActivityQueue> activities(new ActivityQueue);
  auto builder = makeHotSpotVisitor(
      ChainBuilder(*activities, m_createUserActivity)
      );
  std::reference_wrapper<Variant> ref = std::ref(variant);
  HotSpotVariant(ref).apply_visitor(builder);

  return LambdaActivity([activities] (ActivityContext &context) {
      typedef std::function<void (GameContext&)> LambdaSignature;
      auto lambda_ptr = std::make_shared<LambdaSignature>();

      auto lambda = [activities, lambda_ptr] (GameContext &context) {
          context.enqueueAction(UserActivityAction(activities->front()));
          activities->pop();
          if (activities->size() > 0) {
            context.enqueueAction(LambdaAction(*lambda_ptr));
          }
        };
        context.enqueueAction(LambdaAction(*lambda_ptr = lambda));
      });
}



template <class Variant>
void ChainOfResponsibilityUserActivityFactory::ChainBuilder::operator() (
    HotSpot<Variant> const& hotspot) const {

  if (hotspot.hasChildHotSpot()) {
    boost::apply_visitor(
        makeHotSpotVisitor(*this),
        hotspot.getChildHotSpot()
        );
  }
  Variant const& variant = static_cast<Variant const&>(hotspot);
  m_rQueue.push(m_createUserActivity(variant));
}




} // namespace backpack

#endif // BACKPACK_USERACTIVITY_FACTORY_H_


